<?php


namespace App\Repositories;


use App\Jobs\OrderCustomsSend;
use App\Jobs\OrderInvt800;
use App\Jobs\XmlWriteQueue;
use App\Models\Order as OrderModel;
use App\Models\OrderCustoms as Model;
use App\Models\OrderProduct;
use App\Traits\Logs;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use LCustoms\Entry\CEB621Message;
use LCustoms\Entry\CEB625Message;
use LCustoms\Entry\Transfer;
use LCustoms\Message;
use LTools\Repositories\RepositoryAbstract;
use Throwable;

class OrderCustoms extends RepositoryAbstract
{
    use Logs;

    /**
     * OrderCustomsSend constructor.
     * @param Model $model
     */
    public function __construct(Model $model)
    {
        $this->model = $model;
    }

    /**
     * @param array $attributes
     * @param int|null $perPage
     * @param array $columns
     * @param string $pageName
     * @param int|null $page
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function paginate(array $attributes, int $perPage = null, array $columns = ['*'], $pageName = 'page', int $page = null)
    {
        return $this->model->with(['order'])->where(
            $attributes
        )->paginate($perPage, $columns, $pageName, $page);
    }

    /**
     * @param array $attributes
     * @return bool|\Illuminate\Database\Eloquent\Model
     */
    public function create(array $attributes = [])
    {
        $attributes['last_time'] = 0;
        $model = parent::create($attributes);
        // 异步申报海关
        OrderCustomsSend::dispatch($model->getKey());
        return $model;
    }

    /**
     * @param $id
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model
     */
    public function show($id)
    {
        return $this->model->with(['logs'])->findOrFail($id);
    }

    /**
     * @param $id
     * @return Model
     * @throws Exception
     * @throws Throwable
     */
    public function sendCustoms($id)
    {
        try {
            DB::beginTransaction();
            /** @var Model $model */
            $model = $this->model->newQuery()->where('id', '=', $id)->lockForUpdate()->firstOrFail();
            $model->loadMissing('order');

            if ($model->order->getAttribute('status') !== 3) {
                throw new Exception("订单状态不是清关中不能重推");
            }

            if (!in_array($model->getAttribute('status'), [0, 1, 2, 100])) {
                throw new Exception("清单状态不是 待发送 已发送 和退单 不能重推");
            }


            $message = new Message($this->getTransfer());
            $messageString = $message->map([
                $this->getCEB621Message($model->order)
            ], 'CEB625Message');

            $filename = $this->getCEB621MessageFile();
            Storage::put($filename, $messageString);

            $model->setAttribute('file', $filename);
            $model->setAttribute('status', 1);
            $model->saveOrFail();
            // 发
//            XmlWriteQueue::dispatch($messageString);
            // 测试时
            XmlWriteQueue::dispatch($model->order->code);

            DB::commit();
            return $model;
        } catch (ModelNotFoundException $exception) {
            DB::rollBack();
            Log::warning(sprintf("找不到这个清单"));
        } catch (Exception | Throwable $exception) {
            DB::rollBack();
            Log::error(sprintf("清单重推失败[%s]", $exception->getMessage()));
            $model->setAttribute('info', $exception->getMessage());
            $model->saveOrFail();
        }
    }

    /**
     * @param array $values
     * @throws Throwable
     * @author luffyzhao@vip.126.com
     */
    public function returnCustoms(array $values)
    {
        try {
            DB::beginTransaction();
            /** @var OrderModel $order */
            $order = OrderModel::query()->where('code', '=', $values['copNo'])->lockForUpdate()->firstOrFail();

            // 大于 0 的状态才更新订单的状态
            if ($values['returnStatus'] > 0) {
                $resultRow = $order->customs()->where('last_time', '<=', $values['returnTime'])
                    ->where('status', '<>', $values['returnStatus'])
                    ->update([
                        'last_time' => $values['returnTime'],
                        'invt_no' => $values['invtNo'],
                        'pre_no' => $values['preNo'],
                        'status' => $values['returnStatus'],
                        'info' => $values['returnInfo'],
                    ]);

                // 第一个放行回执 并且订单状态是清关中
                if ($resultRow === 1 && $values['returnStatus'] == '800' && $order->getAttribute('status') === 3) {
                    $order->setAttribute('status', 4);
                    $order->saveOrFail();
                    // 生成批次单
                    $orderShipment = $order->shipment()->create([
                        'customer_id' => $order->getAttribute('customer_id'),
                        'info' => $order->products
                    ]);

                    $this->recordLogs($orderShipment, "生成订单出货单");
                    $this->recordLogs($order, "清关完成");
                    // 清关成功后要做的操作
                    OrderInvt800::dispatch($order->getKey());
                }

                // 第一个放行回执 并且订单状态是清关中
                if ($resultRow === 1 && $values['returnStatus'] == '800' && $order->getAttribute('status') === -1) {
                    // 生成修撤单
                    $order->invtRefund()->create([
                        'invt_no' => $values['invtNo'],
                        'pre_no' => $values['preNo'],
                        'last_time' => $values['returnTime'],
                    ]);
                }
            }

            // 有回执就记录
            $order->customs->logs()->create([
                'last_time' => $values['returnTime'],
                'info' => $values['returnInfo'],
            ]);

            DB::commit();
        } catch (ModelNotFoundException $exception) {
            DB::rollBack();
            Log::warning(sprintf("未知的清单回执[%s]", $values['copNo']));
        } catch (Exception | Throwable $exception) {
            DB::rollBack();
            Log::error(sprintf("回执处理失败![%s]", $exception->getMessage()));
        }
    }


    /**
     * @param OrderModel $order
     * @return CEB621Message
     */
    public function getCEB621Message(OrderModel $order)
    {

        $customs = System::getGroup('customs');

        $head = new CEB621Message\InventoryHead();
        $head->orderNo = $order->getAttribute('number');
        $head->ebpCode = $order->getAttribute('ebp_code');
        $head->ebpName = $order->getAttribute('ebp_name');
        $head->ebcCode = $order->getAttribute('ebc_code');
        $head->ebcName = $order->getAttribute('ebc_name');
        $head->logisticsNo = $order->getAttribute('express_code');
        $head->logisticsCode = $order->express->getAttribute('customs_code');
        $head->logisticsName = $order->express->getAttribute('customs_name');
        $head->copNo = $order->getAttribute('code');
        $head->preNo = '';
        // 担保企业
        switch ($order->customer->getAttribute('assure')) {
            case 0:
                $head->assureCode = $order->getAttribute('number');
                break;
            case 2:
                $head->assureCode = $order->getAttribute('customs_code');
                break;
            default:
                $head->assureCode = $order->getAttribute('ebc_code');
        }
        $head->emsNo = $customs['ems_no'];
        $head->invtNo = '';
        $head->ieFlag = 'I';
        $head->declTime = Carbon::now()->format('Ymd');
        $head->customsCode = $customs['customs'];
        $head->portCode = $customs['port'];
        $head->ieDate = Carbon::now()->format('Ymd');
        $head->buyerIdType = $order->getAttribute('buyer_type');
        $head->buyerIdNumber = $order->getAttribute('buyer_number');
        $head->buyerName = $order->getAttribute('buyer_name');
        $head->buyerTelephone = $order->getAttribute('buyer_telephone');
        $head->consigneeAddress = implode(' ', $order->getAttribute('areas')) . $order->getAttribute('address');
        $head->agentCode = $customs['agent_code'];
        $head->agentName = $customs['agent_name'];
        $head->areaCode = $customs['area_code'];
        $head->areaName = $customs['area_name'];
        $head->tradeMode = 1210;
        $head->trafMode = 'W';
        $head->trafNo = '';
        $head->voyageNo = '';
        $head->billNo = '';
        $head->loctNo = '';
        $head->licenseNo = '';
        $head->country = 142;
        $head->freight = $order->getAttribute('freight');
        $head->insuredFee = $order->express->getAttribute('insured');
        $head->currency = 142;
        $head->wrapType = 2;
        $head->packNo = 1;
        $head->grossWeight = $order->getAttribute('weight');
        $head->netWeight = $order->getAttribute('net_weight');
        $head->note = '';
        $cebMessage = new CEB621Message($head);

        $order->products()->each(function (OrderProduct $item, $key) use ($cebMessage) {
            $list = new CEB621Message\InventoryList();
            $list->gnum = $key + 1;
            // 账册序号
            if ($item->product->customs->book) {
                $list->itemRecordNo = $item->product->customs->book->getAttribute('record_no');
            } else {
                throw new Exception(sprintf("产品[%s]账册序号不存在！", $item->product->getAttribute('code')));
            }
            $list->itemNo = $item->product->getAttribute('code');
            $list->itemName = $item->product->getAttribute('title');
            $list->gcode = $item->product->customs->getAttribute('code');
            $list->gmodel = $item->product->customs->getAttribute('element');
            $list->country = $item->product->customs->getAttribute('country');
            $list->currency = 142;
            $list->qty = $item->getAttribute('quantity');
            $list->qty1 = $item->getAttribute('quantity') * $item->product->customs->getAttribute('qy1');
            $list->qty2 = $item->getAttribute('quantity') * $item->product->customs->getAttribute('qy2');
            $list->unit = $item->product->customs->getAttribute('unit');
            $list->unit1 = $item->product->customs->getAttribute('unit1');
            $list->unit2 = $item->product->customs->getAttribute('unit2');
            $list->price = $item->getAttribute('price');
            $list->totalPrice = $item->getAttribute('price') * $item->getAttribute('quantity');

            $cebMessage->addInventoryList($list);
        });


        return $cebMessage;
    }

    /**
     * @return string
     */
    protected function getCEB621MessageFile()
    {
        $filename = Str::uuid()->toString() . '.xml';

        return 'message/CEB621/' . date('ymd/') . substr($filename, 0, 3) . '/' . $filename;
    }

    /**
     * @return Transfer
     */
    protected function getTransfer()
    {
        $customs = System::getGroup('customs');
        $transfer = new Transfer();
        $transfer->dxpId = $customs['dxpid'];
        $transfer->copCode = $customs['agent_code'];
        $transfer->copName = $customs['agent_name'];;
        $transfer->dxpMode = 'DXP';
        return $transfer;
    }

}
